|
1
|
|
|
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */ |
|
2
|
|
|
/* Notes: |
|
3
|
|
|
- usage: `ReactDOM.render( <SheetJSApp />, document.getElementById('app') );` |
|
4
|
|
|
- xlsx.full.min.js is loaded in the head of the HTML page |
|
5
|
|
|
- this script should be referenced with type="text/babel" |
|
6
|
|
|
- babel.js in-browser transpiler should be loaded before this script |
|
7
|
|
|
*/ |
|
8
|
|
|
class SheetJSApp extends React.Component { |
|
9
|
|
|
constructor(props) { |
|
10
|
|
|
super(props); |
|
11
|
|
|
this.state = { |
|
12
|
|
|
data: [], /* Array of Arrays e.g. [["a","b"],[1,2]] */ |
|
13
|
|
|
cols: [] /* Array of column objects e.g. { name: "C", K: 2 } */ |
|
14
|
|
|
}; |
|
15
|
|
|
this.handleFile = this.handleFile.bind(this); |
|
16
|
|
|
this.exportFile = this.exportFile.bind(this); |
|
17
|
|
|
}; |
|
18
|
|
|
handleFile(file/*:File*/) { |
|
19
|
|
|
/* Boilerplate to set up FileReader */ |
|
20
|
|
|
const reader = new FileReader(); |
|
21
|
|
|
const rABS = !!reader.readAsBinaryString; |
|
22
|
|
|
reader.onload = (e) => { |
|
23
|
|
|
/* Parse data */ |
|
24
|
|
|
const bstr = e.target.result; |
|
25
|
|
|
const wb = XLSX.read(bstr, {type:rABS ? 'binary' : 'array'}); |
|
26
|
|
|
/* Get first worksheet */ |
|
27
|
|
|
const wsname = wb.SheetNames[0]; |
|
28
|
|
|
const ws = wb.Sheets[wsname]; |
|
29
|
|
|
/* Convert array of arrays */ |
|
30
|
|
|
const data = XLSX.utils.sheet_to_json(ws, {header:1}); |
|
31
|
|
|
/* Update state */ |
|
32
|
|
|
this.setState({ data: data, cols: make_cols(ws['!ref']) }); |
|
33
|
|
|
}; |
|
34
|
|
|
if(rABS) reader.readAsBinaryString(file); else reader.readAsArrayBuffer(file); |
|
35
|
|
|
}; |
|
36
|
|
|
exportFile() { |
|
37
|
|
|
/* convert state to workbook */ |
|
38
|
|
|
const ws = XLSX.utils.aoa_to_sheet(this.state.data); |
|
39
|
|
|
const wb = XLSX.utils.book_new(); |
|
40
|
|
|
XLSX.utils.book_append_sheet(wb, ws, "SheetJS"); |
|
41
|
|
|
/* generate XLSX file and send to client */ |
|
42
|
|
|
XLSX.writeFile(wb, "sheetjs.xlsx") |
|
43
|
|
|
}; |
|
44
|
|
|
render() { return ( |
|
45
|
|
|
<DragDropFile handleFile={this.handleFile}> |
|
46
|
|
|
<div className="row"><div className="col-xs-12"> |
|
47
|
|
|
<DataInput handleFile={this.handleFile} /> |
|
48
|
|
|
</div></div> |
|
49
|
|
|
<div className="row"><div className="col-xs-12"> |
|
50
|
|
|
<button disabled={!this.state.data.length} className="btn btn-success" onClick={this.exportFile}>Export</button> |
|
51
|
|
|
</div></div> |
|
52
|
|
|
<div className="row"><div className="col-xs-12"> |
|
53
|
|
|
<OutTable data={this.state.data} cols={this.state.cols} /> |
|
54
|
|
|
</div></div> |
|
55
|
|
|
</DragDropFile> |
|
56
|
|
|
); }; |
|
57
|
|
|
}; |
|
58
|
|
|
|
|
59
|
|
|
if(typeof module !== 'undefined') module.exports = SheetJSApp |
|
60
|
|
|
|
|
61
|
|
|
/* -------------------------------------------------------------------------- */ |
|
62
|
|
|
|
|
63
|
|
|
/* |
|
64
|
|
|
Simple HTML5 file drag-and-drop wrapper |
|
65
|
|
|
usage: <DragDropFile handleFile={handleFile}>...</DragDropFile> |
|
66
|
|
|
handleFile(file:File):void; |
|
67
|
|
|
*/ |
|
68
|
|
|
class DragDropFile extends React.Component { |
|
69
|
|
|
constructor(props) { |
|
70
|
|
|
super(props); |
|
71
|
|
|
this.onDrop = this.onDrop.bind(this); |
|
72
|
|
|
}; |
|
73
|
|
|
suppress(evt) { evt.stopPropagation(); evt.preventDefault(); }; |
|
74
|
|
|
onDrop(evt) { evt.stopPropagation(); evt.preventDefault(); |
|
75
|
|
|
const files = evt.dataTransfer.files; |
|
76
|
|
|
if(files && files[0]) this.props.handleFile(files[0]); |
|
77
|
|
|
}; |
|
78
|
|
|
render() { return ( |
|
79
|
|
|
<div onDrop={this.onDrop} onDragEnter={this.suppress} onDragOver={this.suppress}> |
|
80
|
|
|
{this.props.children} |
|
81
|
|
|
</div> |
|
82
|
|
|
); }; |
|
83
|
|
|
}; |
|
84
|
|
|
|
|
85
|
|
|
/* |
|
86
|
|
|
Simple HTML5 file input wrapper |
|
87
|
|
|
usage: <DataInput handleFile={callback} /> |
|
88
|
|
|
handleFile(file:File):void; |
|
89
|
|
|
*/ |
|
90
|
|
|
class DataInput extends React.Component { |
|
91
|
|
|
constructor(props) { |
|
92
|
|
|
super(props); |
|
93
|
|
|
this.handleChange = this.handleChange.bind(this); |
|
94
|
|
|
}; |
|
95
|
|
|
handleChange(e) { |
|
96
|
|
|
const files = e.target.files; |
|
97
|
|
|
if(files && files[0]) this.props.handleFile(files[0]); |
|
98
|
|
|
}; |
|
99
|
|
|
render() { return ( |
|
100
|
|
|
<form className="form-inline"> |
|
101
|
|
|
<div className="form-group"> |
|
102
|
|
|
<label htmlFor="file">Spreadsheet</label> |
|
103
|
|
|
<input type="file" className="form-control" id="file" accept={SheetJSFT} onChange={this.handleChange} /> |
|
104
|
|
|
</div> |
|
105
|
|
|
</form> |
|
106
|
|
|
); }; |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
/* |
|
110
|
|
|
Simple HTML Table |
|
111
|
|
|
usage: <OutTable data={data} cols={cols} /> |
|
112
|
|
|
data:Array<Array<any> >; |
|
113
|
|
|
cols:Array<{name:string, key:number|string}>; |
|
114
|
|
|
*/ |
|
115
|
|
|
class OutTable extends React.Component { |
|
116
|
|
|
constructor(props) { super(props); }; |
|
117
|
|
|
render() { return ( |
|
118
|
|
|
<div className="table-responsive"> |
|
119
|
|
|
<table className="table table-striped"> |
|
120
|
|
|
<thead> |
|
121
|
|
|
<tr>{this.props.cols.map((c) => <th key={c.key}>{c.name}</th>)}</tr> |
|
122
|
|
|
</thead> |
|
123
|
|
|
<tbody> |
|
124
|
|
|
{this.props.data.map((r,i) => <tr key={i}> |
|
125
|
|
|
{this.props.cols.map(c => <td key={c.key}>{ r[c.key] }</td>)} |
|
126
|
|
|
</tr>)} |
|
127
|
|
|
</tbody> |
|
128
|
|
|
</table> |
|
129
|
|
|
</div> |
|
130
|
|
|
); }; |
|
131
|
|
|
}; |
|
132
|
|
|
|
|
133
|
|
|
/* list of supported file types */ |
|
134
|
|
|
const SheetJSFT = [ |
|
135
|
|
|
"xlsx", "xlsb", "xlsm", "xls", "xml", "csv", "txt", "ods", "fods", "uos", "sylk", "dif", "dbf", "prn", "qpw", "123", "wb*", "wq*", "html", "htm" |
|
136
|
|
|
].map(function(x) { return "." + x; }).join(","); |
|
137
|
|
|
|
|
138
|
|
|
/* generate an array of column objects */ |
|
139
|
|
|
const make_cols = refstr => { |
|
140
|
|
|
let o = [], C = XLSX.utils.decode_range(refstr).e.c + 1; |
|
141
|
|
|
for(var i = 0; i < C; ++i) o[i] = {name:XLSX.utils.encode_col(i), key:i} |
|
142
|
|
|
return o; |
|
143
|
|
|
}; |
|
144
|
|
|
|